#include "compiler/compiler.h"
#include "comm.h"
#include "defines.h"
#include "draw_staff.h"
#include <stdio.h>
#include <stdlib.h>
#include <llvm/Support/InstVisitor.h>
#include <llvm/Support/raw_ostream.h>
#include <llvm/Instructions.h>
#include <llvm/IntrinsicInst.h>
#include <llvm/Constants.h>
#include <llvm/Value.h>
#include <llvm/User.h>
#include <llvm/Use.h>
#include <llvm/Function.h>
#include <llvm/BasicBlock.h>
#include <llvm/InstrTypes.h>
#include <llvm/ValueSymbolTable.h>
#include <llvm/Argument.h>
#include <llvm/ADT/ValueMap.h>
#include <iostream>

const unsigned PRIMITIVE_SIZE_IN_BITS = 32;

char buf[20], buf2[20], buf3[20];

enum Location
{
	REG_FILE = 0,
	SURFACE_0,
	SURFACE_1,
	SURFACE_2,
	SURFACE_3
};

struct address
{
	unsigned index_dw;
	enum Location location;
};

unsigned allocateType(const llvm::Type * alloca_type)
{
	llvm::Type::TypeID type_id = alloca_type->getTypeID();
	unsigned num_bits_alloc = alloca_type->getPrimitiveSizeInBits();

	switch (type_id)
	{
	case llvm::Type::VoidTyID:
		{
			break;
		}
	case llvm::Type::FloatTyID:
		{
			break;
		}
	case llvm::Type::IntegerTyID:
		{
			break;
		}
	case llvm::Type::ArrayTyID:
		{
			const llvm::ArrayType * array_type =
				static_cast < const llvm::ArrayType * >(alloca_type);
			unsigned num_ele = array_type->getNumElements();	//getting the size of array

			//recursion
			const llvm::Type * ele_type = alloca_type->getContainedType(0);

			num_bits_alloc = num_ele * allocateType(ele_type);
			break;
		}
	case llvm::Type::PointerTyID:
		break;
	default:
		break;
	}
	return num_bits_alloc;
}

void storeGlobals(llvm::Constant * global_const)
{
	if (llvm::ConstantArray::classof(global_const))
	{
		for (int i = 0; i < global_const->getNumOperands(); ++i)
		{
			storeGlobals(static_cast < llvm::Constant * >(global_const->getOperand(i)));
		}
	}
	else if (llvm::ConstantInt::classof(global_const))
	{
		int const_int =
			static_cast < llvm::ConstantInt * >(global_const)->getValue().getSExtValue();
	}
	else if (llvm::ConstantFP::classof(global_const))
	{
		float const_float =
			const_cast < llvm::APFloat & >(static_cast <
										   llvm::ConstantFP *
										   >(global_const)->getValueAPF()).convertToFloat();
	}
	else
	{
		std::cerr << "Oops! Unrecognized global variable.\n";
	}
}

std::string getAsmBlockLabel(llvm::BasicBlock * const bb)
{
	llvm::Function * func = bb->getParent();
	std::string func_name = func->getName();
	std::string bb_name = bb->getNameStr();

	for (int i = 0; i < bb_name.size(); ++i)
	{
		if (bb_name[i] == '.')
		{
			bb_name[i] = '_';
		}
	}
	return func_name + "__" + bb_name;
}

std::string getAsmSubroutineLabel(llvm::Function * const f)
{
	std::string func_name = f->getNameStr();
	return func_name + "__init";
}

int main()
{

	std::string cl_src;
	char ch;

	FILE *cl_src_file;

	if ((cl_src_file = fopen("./cl_src.cl", "r")) == NULL)
	{
		std::cerr << "Oops! Can't open OpenCL source." << std::endl;
		exit(0);
	}
	while ((ch = fgetc(cl_src_file)) != EOF)
	{
		cl_src.insert(cl_src.end(), 1, ch);
	}
	fclose(cl_src_file);

	Compiler compiler;

	llvm::Module * module = compiler.compile(cl_src);

	char filename[64] = "kernel.g6a";

	std::string kernel_name = "kernel_main";	//kenrel function name
	if (command_init(filename, (kernel_name + "__init").c_str()) != 0)
	{
		std::cerr << "Oops! \"command_init(char* filename)\" failed.\n";
		exit(0);
	}

	// Iterating over the Global Varibles in a module
	llvm::Module::global_iterator g_itr = module->global_begin();
	for (; g_itr != module->global_end(); g_itr++)
	{
		llvm::GlobalVariable * global = g_itr;

		llvm::Constant * global_init = global->getInitializer();

		if (llvm::ConstantArray::classof(global_init))
		{
			llvm::ConstantArray * const_array = static_cast < llvm::ConstantArray * >(global_init);
		}

		storeGlobals(global_init);
	}

	// Iterating over the Functions in a Module
	llvm::Module::iterator f_itr = module->begin();
	for (; f_itr != module->end(); f_itr++)
	{
		llvm::ValueMap < llvm::Value *, address > *value_map =
			new llvm::ValueMap < llvm::Value *, address > ();
		unsigned reg_dw_ptr = 0;	//This pointer indicate how many DWORDs in the reg file have been allocated already

		llvm::Function * func = &*f_itr;	//pointer to Function
		std::string func_name = func->getName();

		if (func->getBasicBlockList().size() == 0)
			continue;

		add_label((func_name + "__init").c_str());	//add label for function

		unsigned arg_cnt = 0;

		if (func_name == kernel_name)
		{
			//--------------------------FIXME-------------------------------
			// Currently, kernel_main() does NOT accept parameters.
		}
		else
		{
			//Iterating over the arguments of a Function
			llvm::Function::arg_iterator arg_itr = func->arg_begin();
			for (; arg_itr != func->arg_end(); ++arg_itr, ++arg_cnt)
			{
				llvm::Argument * arg = arg_itr;
				struct address addr = { reg_dw_ptr, REG_FILE };
				value_map->insert(std::make_pair(arg, addr));
				reg_dw_ptr++;	//advance regfile pointer
			}

			//Popping arguments from stack (write regs)
			macro_pop(0, arg_cnt);
		}
		//std::cout<<"\nFunction (name="<<func_name<<", "<<arg_cnt<<" arguments) has "<<func->size()<<" basic blocks\n";

		// Iterating over the BasicBlocks in a Function
		llvm::Function::iterator b_itr = func->begin();
		for (; b_itr != func->end(); b_itr++)
		{
			llvm::BasicBlock * blk = &*b_itr;
			//std::cout<<"\nBasicBlock (name="<<std::string(blk->getName())<<") has "<<blk->size()<<" instructions\n";

			add_label(getAsmBlockLabel(blk).c_str());	//Adding label for block

			//Iterating over the Instructions in a BasicBlock
			llvm::BasicBlock::iterator i_itr = blk->begin();
			for (; i_itr != blk->end(); i_itr++)
			{
				//Instruction output
				std::string inst_str;
				llvm::raw_string_ostream output(inst_str);
				llvm::Instruction * ins = &*i_itr;
				output << *ins;
				//std::cout<<inst_str<<"\t///// ValueID: "<<ins->getValueID()<<"\t"<<std::endl;

				/* 
				   //Iterating over the users of this value
				   unsigned use_size = 0;
				   llvm::Value::use_iterator u_itr = ins->use_begin();
				   for ( ; u_itr != ins->use_end(); u_itr++)
				   {
				   llvm::User* user = *u_itr;
				   use_size++; 
				   }
				   std::cout<<"\t\tdef-use chain size (use_size): "<<use_size<<std::endl;
				 */

				/* Identifying instruction type */
				std::string opcode_name = ins->getOpcodeName();
				unsigned opcode = ins->getOpcode();

				switch (opcode)
				{
					// Terminator Instructions - These instructions are used to terminate a basic
					// block of the program.   Every basic block must end with one of these
					// instructions for it to be a well formed basic block.
				case 1:		//Ret
					{
						assert(llvm::ReturnInst::classof(ins));
						llvm::ReturnInst * ret_inst = static_cast < llvm::ReturnInst * >(ins);
						llvm::Value * ret_val = ret_inst->getReturnValue();	//ret_val is NULL if there's no return value
						if (ret_val)
						{
							// place return value in designated register (REG_RET)
							command_mov(1,
										REG_RET,
										rnum2rname(buf, value_map->lookup(ret_val).index_dw, d));
						}
						macro_return();	//jump to caller
						break;
					}
				case 2:		//Br
					{
						assert(llvm::BranchInst::classof(ins));
						llvm::BranchInst * br_inst = static_cast < llvm::BranchInst * >(ins);
						if (br_inst->isUnconditional())	//Unconditional Branch - ONE successor
						{
							llvm::BasicBlock * successor = br_inst->getSuccessor(0);
							command_jmpi(1, getAsmBlockLabel(successor).c_str());
						}
						else	//Conditional Branch - TWO successors
						{
							llvm::BasicBlock * successor_true = br_inst->getSuccessor(0);
							llvm::BasicBlock * successor_false = br_inst->getSuccessor(1);
							llvm::Value * cond = br_inst->getCondition();
							assert(!llvm::Constant::classof(cond));	// "cond" cannot be constant in generated llvm IR

							condition(REG_COND);
							command_jmpi(1, getAsmBlockLabel(successor_true).c_str());
							command_jmpi(1, getAsmBlockLabel(successor_false).c_str());
						}
						break;
					}
				case 3:		//Switch
					//break;
				case 4:		//IndirectBr
					//break;
				case 5:		//Invoke
					//break;
				case 6:		//Unwind
					//break;
				case 7:		//Unreachable
					std::cerr << "Oops! Unrecognized instruction type!\n";
					exit(0);
					break;

					// Standard binary operators...
				case 8:		//Add
					{
						assert(llvm::BinaryOperator::classof(ins));
						llvm::BinaryOperator * add_opr =
							static_cast < llvm::BinaryOperator * >(ins);

						//Allocating space for ADD destination
						struct address addr = { reg_dw_ptr, REG_FILE };
						value_map->insert(std::make_pair(ins, addr));

						assert(not(ins->getOperand(0)->getValueID() == llvm::Value::ConstantIntVal and ins->getOperand(1)->getValueID() == llvm::Value::ConstantIntVal));	/* can't be imm & imm */
						if (ins->getOperand(0)->getValueID() == llvm::Value::ConstantIntVal or ins->getOperand(1)->getValueID() == llvm::Value::ConstantIntVal)	/* reg & imm */
						{
							bool op0_is_imm =
								ins->getOperand(0)->getValueID() == llvm::Value::ConstantIntVal;

							llvm::Value * imm = (op0_is_imm ? ins->getOperand(0) : ins->getOperand(1));	//immediate source
							llvm::ConstantInt * const_int =
								static_cast < llvm::ConstantInt * >(imm);
							int ap_int =
								const_cast < llvm::APInt & >(const_int->getValue()).getSExtValue();

							llvm::Value * reg = (op0_is_imm ? ins->getOperand(1) : ins->getOperand(0));	//register source

							command_add(1,
										rnum2rname(buf, reg_dw_ptr, d),
										rnum2rname(buf2, value_map->lookup(reg).index_dw, d),
										inum2str(buf3, ap_int, d));
						}
						else	/* reg & reg */
						{
							llvm::Value * src0 = ins->getOperand(0);
							llvm::Value * src1 = ins->getOperand(1);
							command_add(1,
										rnum2rname(buf, reg_dw_ptr, d),
										rnum2rname(buf2, value_map->lookup(src0).index_dw, d),
										rnum2rname(buf3, value_map->lookup(src1).index_dw, d));
						}

						reg_dw_ptr++;	//advance regfile pointer
						break;
					}
				case 9:		//FAdd
					{
						assert(llvm::BinaryOperator::classof(ins));
						llvm::BinaryOperator * fadd_opr =
							static_cast < llvm::BinaryOperator * >(ins);

						//Allocating space for FADD destination
						struct address addr = { reg_dw_ptr, REG_FILE };
						value_map->insert(std::make_pair(ins, addr));

						assert(not(ins->getOperand(0)->getValueID() == llvm::Value::ConstantFPVal and ins->getOperand(1)->getValueID() == llvm::Value::ConstantFPVal));	/* can't be imm & imm */
						if (ins->getOperand(0)->getValueID() == llvm::Value::ConstantFPVal or ins->getOperand(1)->getValueID() == llvm::Value::ConstantFPVal)	/* reg & imm */
						{
							bool op0_is_imm =
								ins->getOperand(0)->getValueID() == llvm::Value::ConstantFPVal;

							llvm::Value * imm =
								(op0_is_imm ? ins->getOperand(0) : ins->getOperand(1));
							llvm::ConstantFP * const_float =
								static_cast < llvm::ConstantFP * >(imm);
							float ap_float =
								const_cast <
								llvm::APFloat & >(const_float->getValueAPF()).convertToFloat();

							llvm::Value * reg =
								(op0_is_imm ? ins->getOperand(1) : ins->getOperand(0));
							command_add(1, rnum2rname(buf, reg_dw_ptr, f),
										rnum2rname(buf2, value_map->lookup(reg).index_dw, f),
										float2str(buf3, ap_float));
						}
						else	/* reg & reg */
						{
							llvm::Value * src0 = ins->getOperand(0);
							llvm::Value * src1 = ins->getOperand(1);
							command_add(1,
										rnum2rname(buf, reg_dw_ptr, f),
										rnum2rname(buf2, value_map->lookup(src0).index_dw, f),
										rnum2rname(buf3, value_map->lookup(src1).index_dw, f));
						}

						reg_dw_ptr++;	//advance regfile pointer
						break;
					}
				case 10:		//Sub
					{
						assert(llvm::BinaryOperator::classof(ins));
						llvm::BinaryOperator * sub_opr =
							static_cast < llvm::BinaryOperator * >(ins);

						//Allocating space for SUB destination
						struct address addr = { reg_dw_ptr, REG_FILE };
						value_map->insert(std::make_pair(ins, addr));

						assert(not(ins->getOperand(0)->getValueID() == llvm::Value::ConstantIntVal and ins->getOperand(1)->getValueID() == llvm::Value::ConstantIntVal));	/* can't be imm & imm */
						if (ins->getOperand(0)->getValueID() == llvm::Value::ConstantIntVal)	// sub <imm> <reg>
						{
							llvm::Value * imm = ins->getOperand(0);	//immediate source
							llvm::ConstantInt * const_int =
								static_cast < llvm::ConstantInt * >(imm);
							int ap_int =
								const_cast < llvm::APInt & >(const_int->getValue()).getSExtValue();

							llvm::Value * reg = ins->getOperand(1);	//register source

							command_add(1,
										rnum2rname(buf, reg_dw_ptr, d),
										rnum2rname(buf2, value_map->lookup(reg).index_dw, d, 0),
										inum2str(buf3, ap_int, d));
						}
						else if (ins->getOperand(1)->getValueID() == llvm::Value::ConstantIntVal)	// sub <reg> <imm>
						{
							llvm::Value * imm = ins->getOperand(1);
							llvm::ConstantInt * const_int =
								static_cast < llvm::ConstantInt * >(imm);
							int ap_int =
								const_cast < llvm::APInt & >(const_int->getValue()).getSExtValue();

							llvm::Value * reg = ins->getOperand(0);	//register source

							command_add(1,
										rnum2rname(buf, reg_dw_ptr, d),
										rnum2rname(buf2, value_map->lookup(reg).index_dw, d),
										inum2str(buf3, ap_int, d, 0));
						}
						else	/* reg & reg */
						{
							llvm::Value * src0 = ins->getOperand(0);
							llvm::Value * src1 = ins->getOperand(1);
							command_add(1,
										rnum2rname(buf, reg_dw_ptr, d),
										rnum2rname(buf2, value_map->lookup(src0).index_dw, d),
										rnum2rname(buf3, value_map->lookup(src1).index_dw, d, 0));
						}

						reg_dw_ptr++;	//advance regfile pointer
						break;
					}
				case 11:		//FSub
					{
						assert(llvm::BinaryOperator::classof(ins));
						llvm::BinaryOperator * fsub_opr =
							static_cast < llvm::BinaryOperator * >(ins);

						//Allocating space for FSub destination
						struct address addr = { reg_dw_ptr, REG_FILE };
						value_map->insert(std::make_pair(ins, addr));

						assert(not(ins->getOperand(0)->getValueID() == llvm::Value::ConstantFPVal and ins->getOperand(1)->getValueID() == llvm::Value::ConstantFPVal));	/* can't be imm & imm */
						if (ins->getOperand(0)->getValueID() == llvm::Value::ConstantFPVal)	// fsub <imm> <reg>
						{
							llvm::Value * imm = ins->getOperand(0);
							llvm::ConstantFP * const_float =
								static_cast < llvm::ConstantFP * >(imm);
							float ap_float =
								const_cast <
								llvm::APFloat & >(const_float->getValueAPF()).convertToFloat();

							llvm::Value * reg = ins->getOperand(1);
							command_add(1,
										rnum2rname(buf, reg_dw_ptr, f),
										rnum2rname(buf2, value_map->lookup(reg).index_dw, f, 0),
										float2str(buf3, ap_float));
						}
						else if (ins->getOperand(1)->getValueID() == llvm::Value::ConstantFPVal)	// fsub <reg> <imm>
						{
							llvm::Value * imm = ins->getOperand(1);
							llvm::ConstantFP * const_float =
								static_cast < llvm::ConstantFP * >(imm);
							float ap_float =
								const_cast <
								llvm::APFloat & >(const_float->getValueAPF()).convertToFloat();

							llvm::Value * reg = ins->getOperand(0);
							command_add(1,
										rnum2rname(buf, reg_dw_ptr, f),
										rnum2rname(buf2, value_map->lookup(reg).index_dw, f),
										float2str(buf3, ap_float, 0));

						}
						else	/* reg & reg */
						{
							llvm::Value * src0 = ins->getOperand(0);
							llvm::Value * src1 = ins->getOperand(1);
							command_add(1,
										rnum2rname(buf, reg_dw_ptr, f),
										rnum2rname(buf2, value_map->lookup(src0).index_dw, f),
										rnum2rname(buf3, value_map->lookup(src1).index_dw, f, 0));
						}

						reg_dw_ptr++;	//advance regfile pointer
						break;
					}
				case 12:		//Mul
					{
						assert(llvm::BinaryOperator::classof(ins));
						llvm::BinaryOperator * mul_opr =
							static_cast < llvm::BinaryOperator * >(ins);

						//Allocating space for MUL destination
						struct address addr = { reg_dw_ptr, REG_FILE };
						value_map->insert(std::make_pair(ins, addr));

						assert(not(ins->getOperand(0)->getValueID() == llvm::Value::ConstantIntVal and ins->getOperand(1)->getValueID() == llvm::Value::ConstantIntVal));	/* can't be imm & imm */
						if (ins->getOperand(0)->getValueID() == llvm::Value::ConstantIntVal or ins->getOperand(1)->getValueID() == llvm::Value::ConstantIntVal)	/* reg & imm */
						{
							bool op0_is_imm =
								ins->getOperand(0)->getValueID() == llvm::Value::ConstantIntVal;

							llvm::Value * imm = (op0_is_imm ? ins->getOperand(0) : ins->getOperand(1));	//immediate source
							llvm::ConstantInt * const_int =
								static_cast < llvm::ConstantInt * >(imm);
							int ap_int =
								const_cast < llvm::APInt & >(const_int->getValue()).getSExtValue();

							llvm::Value * reg = (op0_is_imm ? ins->getOperand(1) : ins->getOperand(0));	//register source

							command_mul(1,
										rnum2rname(buf, reg_dw_ptr, d),
										rnum2rname(buf2, value_map->lookup(reg).index_dw, d),
										inum2str(buf3, ap_int, d));
						}
						else	/* reg & reg */
						{
							llvm::Value * src0 = ins->getOperand(0);
							llvm::Value * src1 = ins->getOperand(1);
							command_mul(1,
										rnum2rname(buf, reg_dw_ptr, d),
										rnum2rname(buf2, value_map->lookup(src0).index_dw, d),
										rnum2rname(buf3, value_map->lookup(src1).index_dw, d));
						}

						reg_dw_ptr++;	//advance regfile pointer
						break;
					}
				case 13:		//FMul
					{
						assert(llvm::BinaryOperator::classof(ins));
						llvm::BinaryOperator * fmul_opr =
							static_cast < llvm::BinaryOperator * >(ins);

						//Allocating space for FMUL destination
						struct address addr = { reg_dw_ptr, REG_FILE };
						value_map->insert(std::make_pair(ins, addr));

						assert(not(ins->getOperand(0)->getValueID() == llvm::Value::ConstantFPVal and ins->getOperand(1)->getValueID() == llvm::Value::ConstantFPVal));	/* can't be imm & imm */
						if (ins->getOperand(0)->getValueID() == llvm::Value::ConstantFPVal or ins->getOperand(1)->getValueID() == llvm::Value::ConstantFPVal)	/* reg & imm */
						{
							bool op0_is_imm =
								ins->getOperand(0)->getValueID() == llvm::Value::ConstantFPVal;

							llvm::Value * imm =
								(op0_is_imm ? ins->getOperand(0) : ins->getOperand(1));
							llvm::ConstantFP * const_float =
								static_cast < llvm::ConstantFP * >(imm);
							float ap_float =
								const_cast <
								llvm::APFloat & >(const_float->getValueAPF()).convertToFloat();

							llvm::Value * reg =
								(op0_is_imm ? ins->getOperand(1) : ins->getOperand(0));
							command_mul(1, rnum2rname(buf, reg_dw_ptr, f),
										rnum2rname(buf2, value_map->lookup(reg).index_dw, f),
										float2str(buf3, ap_float));
						}
						else	/* reg & reg */
						{
							llvm::Value * src0 = ins->getOperand(0);
							llvm::Value * src1 = ins->getOperand(1);
							command_mul(1,
										rnum2rname(buf, reg_dw_ptr, f),
										rnum2rname(buf2, value_map->lookup(src0).index_dw, f),
										rnum2rname(buf3, value_map->lookup(src1).index_dw, f));
						}

						reg_dw_ptr++;	//advance regfile pointer

						break;
					}
				case 14:		//UDiv
					std::cerr << "Oops! Unrecognized instruction type.\n";
					exit(0);
					break;
				case 15:		//SDiv
					{
						assert(llvm::BinaryOperator::classof(ins));
						llvm::BinaryOperator * sdiv_opr =
							static_cast < llvm::BinaryOperator * >(ins);

						//Allocating space for SDiv destination
						struct address addr = { reg_dw_ptr, REG_FILE };
						value_map->insert(std::make_pair(ins, addr));

						char sdiv_arg[2][20];

						for (int i = 0; i < ins->getNumOperands(); ++i)
						{
							llvm::Value * src = ins->getOperand(i);
							if (llvm::Constant::classof(src))	// source operand is constant
							{
								int src_int =
									const_cast < llvm::APInt & >(static_cast <
																 llvm::ConstantInt *
																 >(src)->getValue()).getSExtValue();
								inum2str(sdiv_arg[i], src_int, d);
							}
							else	// source operand is not constant
							{
								rnum2rname(sdiv_arg[i], value_map->lookup(src).index_dw, d);
							}
						}

						command_divint(1, rnum2rname(buf, reg_dw_ptr, d), sdiv_arg[0], sdiv_arg[1]);

						reg_dw_ptr++;
						break;
					}
				case 16:		//FDiv
					{
						assert(llvm::BinaryOperator::classof(ins));
						llvm::BinaryOperator * fdiv_opr =
							static_cast < llvm::BinaryOperator * >(ins);

						//Allocating space for FDiv destination
						struct address addr = { reg_dw_ptr, REG_FILE };
						value_map->insert(std::make_pair(ins, addr));

						char fdiv_arg[2][20];

						for (int i = 0; i < ins->getNumOperands(); ++i)
						{
							llvm::Value * src = ins->getOperand(i);
							if (llvm::Constant::classof(src))	// source operand is constant
							{
								float src_float =
									const_cast < llvm::APFloat & >(static_cast <
																   llvm::ConstantFP *
																   >(src)->
																   getValueAPF()).convertToFloat();
								float2str(fdiv_arg[i], src_float);
							}
							else	// source operand is not constant
							{
								rnum2rname(fdiv_arg[i], value_map->lookup(src).index_dw, f);
							}
						}

						command_divfloat(1,
										 rnum2rname(buf, reg_dw_ptr, f), fdiv_arg[0], fdiv_arg[1]);

						reg_dw_ptr++;
						break;
					}
				case 17:		//URem
					std::cerr << "Oops! Unrecognized instruction type.\n";
					exit(0);
					break;
				case 18:		//SRem
					{
						assert(llvm::BinaryOperator::classof(ins));
						llvm::BinaryOperator * sdiv_opr =
							static_cast < llvm::BinaryOperator * >(ins);

						//Allocating space for SRem destination
						struct address addr = { reg_dw_ptr, REG_FILE };
						value_map->insert(std::make_pair(ins, addr));

						char srem_arg[2][20];

						for (int i = 0; i < ins->getNumOperands(); ++i)
						{
							llvm::Value * src = ins->getOperand(i);
							if (llvm::Constant::classof(src))	// source operand is constant
							{
								int src_int =
									const_cast < llvm::APInt & >(static_cast <
																 llvm::ConstantInt *
																 >(src)->getValue()).getSExtValue();
								inum2str(srem_arg[i], src_int, d);
							}
							else	// source operand is not constant
							{
								rnum2rname(srem_arg[i], value_map->lookup(src).index_dw, d);
							}
						}

						command_modint(1, rnum2rname(buf, reg_dw_ptr, d), srem_arg[0], srem_arg[1]);

						reg_dw_ptr++;
						break;
					}
				case 19:		//FRem
					//break;

					// Logical operators (integer operands)
				case 20:		//Shl
					//break;
				case 21:		//LShr
					//break;
				case 22:		//AShr
					//break;
				case 23:		//And
					//break;
				case 24:		//Or
					//break;
				case 25:		//Xor
					std::cerr << "Oops! Unrecognized instruction type." << std::endl;
					break;

					// Memory operators
				case 26:		//Alloca
					{
						assert(llvm::AllocaInst::classof(ins));
						llvm::AllocaInst * alloca_ins = static_cast < llvm::AllocaInst * >(ins);

						//Identifying allocated type
						const llvm::Type * const alloca_type = alloca_ins->getAllocatedType();

						unsigned num_primitive_ele = 1;

						const llvm::Type * ele_type = alloca_type;

						while (ele_type->isArrayTy())
						{
							num_primitive_ele *=
								static_cast < const llvm::ArrayType * >(ele_type)->getNumElements();
							ele_type =
								static_cast < const llvm::ArrayType * >(ele_type)->getElementType();
						}

						//Allocating space for ALLOCA destination
						struct address addr = { reg_dw_ptr, REG_FILE };
						value_map->insert(std::make_pair(ins, addr));
						command_mov(1, rnum2rname(buf, reg_dw_ptr, ud), inum2str(buf2, rnum2addr(reg_dw_ptr + 1), ud));	// R(no.reg_dw_ptr) = reg_dw_ptr+1;
						reg_dw_ptr++;

						//Allocating ALLOCA'ed space
						reg_dw_ptr += num_primitive_ele;	//advance regfile pointer

						break;
					}
				case 27:		//Load
					{
						assert(llvm::LoadInst::classof(ins));
						llvm::LoadInst * load_inst = static_cast < llvm::LoadInst * >(ins);
						llvm::Value * ptr_operand = load_inst->getPointerOperand();
						assert(ptr_operand->getType()->isPointerTy());

						//Allocating space for LOAD destination
						struct address addr = { reg_dw_ptr, REG_FILE };
						value_map->insert(std::make_pair(ins, addr));

						command_mov(1, REG_ADDR, rnum2rname(buf, value_map->lookup(ptr_operand).index_dw, ud));	// addr
						command_mov(1, rnum2rname(buf, reg_dw_ptr, ud), REG_INDEX(ud));	// load a integer number

						reg_dw_ptr++;	//advance regfile pointer
						break;
					}
				case 28:		//Store
					{
						assert(llvm::StoreInst::classof(ins));
						llvm::StoreInst * store_inst = static_cast < llvm::StoreInst * >(ins);
						llvm::Value * val_operand = store_inst->getValueOperand();
						llvm::Value * ptr_operand = store_inst->getPointerOperand();

						command_mov(1, REG_ADDR, rnum2rname(buf, value_map->lookup(ptr_operand).index_dw, ud));	//areg
						if (llvm::Constant::classof(val_operand))	// store <imm> <reg>
						{
							if (llvm::ConstantInt::classof(val_operand))
							{
								int int_val =
									static_cast <
									llvm::ConstantInt * >(val_operand)->getValue().getSExtValue();
								command_mov(1, REG_INDEX(ud), inum2str(buf, int_val, ud));
							}
							else if (llvm::ConstantFP::classof(val_operand))
							{
								float fp_val =
									const_cast < llvm::APFloat & >(static_cast <
																   llvm::ConstantFP *
																   >(val_operand)->
																   getValueAPF()).convertToFloat();
								command_mov(1, REG_INDEX(f), float2str(buf, fp_val));
							}
						}
						else	// store <reg> <reg>
						{
							command_mov(1, REG_INDEX(ud), rnum2rname(buf, value_map->lookup(val_operand).index_dw, ud));	// store
						}

						break;
					}
				case 29:		//GetElementPtr
					{
						assert(llvm::GetElementPtrInst::classof(ins));
						llvm::GetElementPtrInst * gep_ins =
							static_cast < llvm::GetElementPtrInst * >(ins);
						llvm::Value * ptr_operand = gep_ins->getPointerOperand();

						if (gep_ins->getNumIndices() == 2 &&
							static_cast <
							llvm::ConstantInt * >(ins->getOperand(1))->getValue().getSExtValue() ==
							0
							&& static_cast <
							llvm::ConstantInt * >(ins->getOperand(2))->getValue().getSExtValue() ==
							0)
							//GEP instruction is in the form of: %arraydecay = getelementptr ptr_operand, i32 0, i32 0 ;
						{
							struct address addr = { reg_dw_ptr, REG_FILE };
							value_map->insert(std::make_pair(gep_ins, addr));
							command_mov(1,
										rnum2rname(buf, reg_dw_ptr, d),
										rnum2rname(buf2,
												   value_map->lookup(ptr_operand).index_dw, d));
							reg_dw_ptr++;
							//std::cout<<"    "<<value_map->lookup(ptr_operand).index_dw<<std::endl;
						}
						else if (gep_ins->getNumIndices() == 1)
							//GEP instruction is in the form of: %arrayidx = getelementptr ptr_operand, i32 x ;
						{
							const llvm::Type * ele_type =
								static_cast <
								const llvm::PointerType *
								>(ptr_operand->getType())->getElementType();
							unsigned num_primitive_ele = 1;

							while (ele_type->isArrayTy())
							{
								num_primitive_ele *=
									static_cast <
									const llvm::ArrayType * >(ele_type)->getNumElements();
								ele_type =
									static_cast <
									const llvm::ArrayType * >(ele_type)->getElementType();
							}

							llvm::Value * offset = ins->getOperand(1);
							if (llvm::Constant::classof(offset))	// offset is constant
							{
								unsigned num_dw_ahead = num_primitive_ele *
									static_cast <
									llvm::ConstantInt *
									>(ins->getOperand(1))->getValue().getSExtValue();

								unsigned num_byte_ahead = num_dw_ahead * 4;	//BYTE_PER_DW = 4

								struct address addr = { reg_dw_ptr, REG_FILE };
								value_map->insert(std::make_pair(gep_ins, addr));
								command_add(1,
											rnum2rname(buf, reg_dw_ptr, d),
											rnum2rname(buf2,
													   value_map->lookup(ptr_operand).index_dw, d),
											inum2str(buf3, num_byte_ahead, d));
							}
							else
							{
								struct address addr = { reg_dw_ptr, REG_FILE };
								value_map->insert(std::make_pair(gep_ins, addr));
								command_mul(1, rnum2rname(buf, reg_dw_ptr, d), rnum2rname(buf2, value_map->lookup(offset).index_dw, d), inum2str(buf3, 4, d));	//BYTE_PER_DW = 4
								command_add(1,
											rnum2rname(buf, reg_dw_ptr, d),
											rnum2rname(buf2, reg_dw_ptr, d),
											rnum2rname(buf3,
													   value_map->lookup(ptr_operand).index_dw, d));
							}
							reg_dw_ptr++;
						}
						else
						{
							std::cerr <<
								"Oops! Unrecognized format of getelementptr instrunction.\n";
						}

						break;
					}

					// Cast operators
				case 30:		//Trunc
				case 31:		//ZExt
				case 32:		//SExt
				case 33:		//FPToUI
				case 34:		//FPToSI
				case 35:		//UIToFP
				case 36:		//SIToFP
				case 37:		//FPTrunc
				case 38:		//FPExt
				case 39:		//PtrToInt
				case 40:		//IntToPtr
				case 41:		//BitCast
					std::cerr << "Oops! Unrecognized instruction type.\n";
					exit(0);
					break;

					// Other operators
				case 42:		//ICmp
					{
						assert(llvm::ICmpInst::classof(ins));
						llvm::ICmpInst * icmp_ins = static_cast < llvm::ICmpInst * >(ins);

						//Identifying predicate
						llvm::CmpInst::Predicate pred = icmp_ins->getPredicate();
						if (ins->getOperand(0)->getValueID() == llvm::Value::ConstantIntVal)
						{
							// Assembly can accept immediate only as the 2nd src operand.
							// So if the 1st src operand in llvm IR is immediate,
							// swapping is required.
							pred = icmp_ins->getSwappedPredicate();
						}

						const char *cond;

						switch (pred)
						{
						case llvm::CmpInst::ICMP_EQ:
							{
								cond = EQUAL;
								break;
							}
						case llvm::CmpInst::ICMP_SGT:
							{
								cond = GREATER;
								break;
							}
						case llvm::CmpInst::ICMP_SGE:
							{
								cond = GREATER_EQUAL;
								break;
							}
						case llvm::CmpInst::ICMP_SLT:
							{
								cond = LESS;
								break;
							}
						case llvm::CmpInst::ICMP_SLE:
							{
								cond = LESS_EQUAL;
								break;
							}
						case llvm::CmpInst::ICMP_NE:
							{
								cond = NOT_EQUAL;
								break;
							}
						default:
							{
								std::cerr << "Oops! Unrecognized predicate.\n";
								exit(0);
							}
						}

						assert(not(ins->getOperand(0)->getValueID() == llvm::Value::ConstantIntVal and ins->getOperand(1)->getValueID() == llvm::Value::ConstantIntVal));	/* can't be imm & imm */
						if (ins->getOperand(0)->getValueID() == llvm::Value::ConstantIntVal or ins->getOperand(1)->getValueID() == llvm::Value::ConstantIntVal)	/* reg & imm */
						{
							bool op0_is_imm =
								ins->getOperand(0)->getValueID() == llvm::Value::ConstantIntVal;

							llvm::Value * imm =
								(op0_is_imm ? ins->getOperand(0) : ins->getOperand(1));
							llvm::ConstantInt * const_int =
								static_cast < llvm::ConstantInt * >(imm);
							int ap_int =
								const_cast < llvm::APInt & >(const_int->getValue()).getSExtValue();

							llvm::Value * reg =
								(op0_is_imm ? ins->getOperand(1) : ins->getOperand(0));

							command_cmp(1,
										cond,
										REG_COND,
										rnum2rname(buf, value_map->lookup(reg).index_dw, d),
										inum2str(buf2, ap_int, d));
						}
						else	/* reg & reg */
						{
							llvm::Value * src0 = ins->getOperand(0);
							llvm::Value * src1 = ins->getOperand(1);

							command_cmp(1, cond, REG_COND, rnum2rname(buf, value_map->lookup(src0).index_dw, d), rnum2rname(buf2, value_map->lookup(src1).index_dw, d));	// modify flag register
						}

						break;
					}
				case 43:		//FCmp
					{
						assert(llvm::FCmpInst::classof(ins));
						llvm::FCmpInst * fcmp_ins = static_cast < llvm::FCmpInst * >(ins);

						// Identifying predicate
						llvm::CmpInst::Predicate pred = fcmp_ins->getPredicate();
						if (ins->getOperand(0)->getValueID() == llvm::Value::ConstantFPVal)
						{
							// Assembly can accept immediate only as the 2nd src operand.
							// So if the 1st src operand in llvm IR is immediate,
							// swapping is required.
							pred = fcmp_ins->getSwappedPredicate();
						}

						const char *cond;

						switch (pred)
						{
						case llvm::CmpInst::FCMP_OEQ:
							{
								cond = EQUAL;
								break;
							}
						case llvm::CmpInst::FCMP_OGT:
							{
								cond = GREATER;
								break;
							}
						case llvm::CmpInst::FCMP_OGE:
							{
								cond = GREATER_EQUAL;
								break;
							}
						case llvm::CmpInst::FCMP_OLT:
							{
								cond = LESS;
								break;
							}
						case llvm::CmpInst::FCMP_OLE:
							{
								cond = LESS_EQUAL;
								break;
							}
						case llvm::CmpInst::FCMP_ONE:
							{
								cond = NOT_EQUAL;
								break;
							}
						default:
							{
								std::cerr << "Oops! Unrecognized predicate.\n";
								exit(0);
							}
						}

						assert(not(ins->getOperand(0)->getValueID() == llvm::Value::ConstantFPVal and ins->getOperand(1)->getValueID() == llvm::Value::ConstantFPVal));	/* can't be imm & imm */
						if (ins->getOperand(0)->getValueID() == llvm::Value::ConstantFPVal or ins->getOperand(1)->getValueID() == llvm::Value::ConstantFPVal)	/* reg & imm */
						{
							bool op0_is_imm =
								ins->getOperand(0)->getValueID() == llvm::Value::ConstantFPVal;

							llvm::Value * imm =
								(op0_is_imm ? ins->getOperand(0) : ins->getOperand(1));
							llvm::ConstantFP * const_float =
								static_cast < llvm::ConstantFP * >(imm);
							float ap_float =
								const_cast <
								llvm::APFloat & >(const_float->getValueAPF()).convertToFloat();

							llvm::Value * reg =
								(op0_is_imm ? ins->getOperand(1) : ins->getOperand(0));

							command_cmp(1,
										cond,
										REG_COND,
										rnum2rname(buf, value_map->lookup(reg).index_dw, f),
										float2str(buf2, ap_float));
						}
						else	/* reg & reg */
						{
							llvm::Value * src0 = ins->getOperand(0);
							llvm::Value * src1 = ins->getOperand(1);

							command_cmp(1, cond, REG_COND, rnum2rname(buf, value_map->lookup(src0).index_dw, f), rnum2rname(buf2, value_map->lookup(src1).index_dw, f));	// modify flag register
						}

						break;
					}
				case 44:		//PHI
					std::cerr << "Oops! Unrecognized instruction type!\n";
					exit(0);
					break;
				case 45:		//Call
					{
						assert(llvm::CallInst::classof(ins));
						llvm::CallInst * call_ins = static_cast < llvm::CallInst * >(ins);

						llvm::Function * called_func = call_ins->getCalledFunction();

						if (llvm::IntrinsicInst::classof(call_ins))	// llvm intrinsic instruction
						{
							if (llvm::
								MemCpyInst::classof(static_cast <
													llvm::IntrinsicInst * >(call_ins)))
								// llvm.memcpy intrinsic function call
							{
								llvm::Value * src_val =
									(static_cast <
									 llvm::MemTransferInst * >(call_ins))->getSource();
								llvm::Value * dest_val =
									(static_cast < llvm::MemIntrinsic * >(call_ins))->getDest();
								llvm::Value * len_val =
									(static_cast < llvm::MemIntrinsic * >(call_ins))->getLength();
								unsigned len_in_bytes =
									static_cast <
									llvm::ConstantInt * >(len_val)->getValue().getSExtValue();
							}
							else
							{
								std::cerr << "Unrecognized llvm intrinsic function call!\n";
								exit(0);
							}
						}
						else if (called_func->getNameStr() == "cl_draw_point")
						{
							if (call_ins->getNumOperands() != 3)
							{
								std::cerr <<
									"Oops! Function cl_draw_point accepts exactly 2 arguments.\n";
								exit(0);
							}
							// Iterating over the Arguments of the function
							char draw_point_arg[2][20];

							for (int i = 1; i < call_ins->getNumOperands(); ++i)
							{
								llvm::Value * arg_val = call_ins->getOperand(i);

								if (llvm::Constant::classof(arg_val))	//argument is constant number
								{
									if (llvm::ConstantInt::classof(arg_val))
									{
										int arg_int =
											static_cast <
											llvm::ConstantInt *
											>(arg_val)->getValue().getSExtValue();
										inum2str(draw_point_arg[i - 1], arg_int, uw);
									}
									else
									{
										std::cerr << "Oops! cl_draw_int(int, int).\n";
										exit(0);
									}
								}
								else	//argument is NOT constant number
								{
									if (arg_val->getType()->isIntegerTy())
									{
										rnum2rname(draw_point_arg[i - 1],
												   value_map->lookup(arg_val).index_dw, uw);
									}
									else
									{
										std::cerr << "Oops! cl_draw_int(int, int)\n";
										exit(0);
									}
								}
							}

							draw_point(draw_point_arg[0], draw_point_arg[1]);
						}
						else if (called_func->getNameStr() == "cl_set_color")
						{
							if (call_ins->getNumOperands() != 4)
							{
								std::cerr <<
									"Oops! Function cl_set_color accepts exactly 3 arguments.\n";
								exit(0);
							}
							// Iterating over the Arguments of the function
							char set_color_arg[3][20];

							for (int i = 1; i < call_ins->getNumOperands(); ++i)
							{
								llvm::Value * arg_val = call_ins->getOperand(i);

								if (llvm::Constant::classof(arg_val))	//argument is constant number
								{
									if (llvm::ConstantInt::classof(arg_val))
									{
										int arg_int =
											static_cast <
											llvm::ConstantInt *
											>(arg_val)->getValue().getSExtValue();
										inum2str(set_color_arg[i - 1], arg_int, uw);
									}
									else
									{
										std::cerr << "Oops! cl_set_color(int, int, int).\n";
										exit(0);
									}
								}
								else	//argument is NOT constant number
								{
									if (arg_val->getType()->isIntegerTy())
									{
										rnum2rname(set_color_arg[i - 1],
												   value_map->lookup(arg_val).index_dw, uw);
									}
									else
									{
										std::cerr << "Oops! cl_set_color(int, int, int)\n";
										exit(0);
									}
								}
							}

							graph_set_color(set_color_arg[0], set_color_arg[1], set_color_arg[2]);
						}
						else	//non-intrinsic function call
						{
							// Saving current IP
							push_linkage();

							// Pushing register file into stack
							macro_push(0, reg_dw_ptr);

							// Pushing arguments into stack
							// Iterating over the Arguments of the function
							for (int i = 1; i < call_ins->getNumOperands(); ++i)
							{
								llvm::Value * arg_val = call_ins->getOperand(i);

								if (llvm::Constant::classof(arg_val))	//argument is constant number
								{
									if (llvm::ConstantInt::classof(arg_val))
									{
										int arg_int =
											static_cast <
											llvm::ConstantInt *
											>(arg_val)->getValue().getSExtValue();
										push_param(inum2str(buf, arg_int, d));
									}
									else if (llvm::ConstantFP::classof(arg_val))
									{
										float arg_float =
											const_cast < llvm::APFloat & >(static_cast <
																		   llvm::ConstantFP *
																		   >(arg_val)->getValueAPF
																		   ()).convertToFloat();
										push_param(float2str(buf, arg_float));
									}
								}
								else	//argument is NOT constant number
								{
									//std::cout<<"    "<<rnum2rname(buf, value_map->lookup(arg_val).index_dw, d)<<std::endl;
									push_param(rnum2rname(buf,
														  value_map->lookup(arg_val).index_dw, d));
								}
							}

							// Jump to subroutine
							macro_call(getAsmSubroutineLabel(called_func).c_str());

							// Pop stack into register file
							macro_pop(0, reg_dw_ptr);

							// Retain IP
							pop_linkage();

							// Get Returned Value
							if (!called_func->getReturnType()->isVoidTy())	// called function has return value
							{
								struct address addr = { reg_dw_ptr, REG_FILE };
								value_map->insert(std::make_pair(call_ins, addr));
								command_mov(1, rnum2rname(buf, reg_dw_ptr, d), REG_RET);
								reg_dw_ptr++;
							}
							else	// no return value
							{
							}
						}
						break;
					}
				case 46:		//Select
				case 47:		//UserOp1
				case 48:		//UserOp2
				case 49:		//VAArg
				case 50:		//ExtractElement
				case 51:		//InsertElemnt
				case 52:		//ShuffleCector
				case 53:		//ExtractValue
				case 54:		//InsertValue
				default:
					std::cerr << "Oops! Unrecognized instruction type.\n";
					exit(0);
					break;
				}
			}
		}
	}

	FILE *llvm_ir_file;

	if ((llvm_ir_file = fopen("llvm_ir.ll", "w")) == NULL)
	{
		std::cerr << "Oops! Can't write to llvm ir file." << std::endl;
		exit(0);
	}

	std::string module_str;
	llvm::raw_string_ostream output(module_str);
	output << *module;
	fprintf(llvm_ir_file, "\n%s\n\n", module_str.c_str());
	fclose(llvm_ir_file);

	command_terminate();
	return 0;
}
